#include "heap.h"
#include "user_bsp_cfg.h"

#if HEAP_USE_EXSRAM
// 内存池(32字节对齐)
__align(32) uint8_t exsrambase[EXSRAM_MAX_SIZE] __attribute__((at(0X68000000)));
uint8_t exsrammapbase[EXSRAM_ALLOC_TABLE_SIZE] __attribute__((at(0X68000000 + EXSRAM_MAX_SIZE)));
#endif

__align(32) uint8_t ccmbase[CCM_MAX_SIZE] __attribute__((at(0X10000000)));
uint8_t ccmmapbase[CCM_ALLOC_TABLE_SIZE] __attribute__((at(0X10000000 + CCM_MAX_SIZE)));

/*CCM内存管理控制块*/
heap_dev_t __ccm = {
    .rambase = ccmbase,
    .mapbase = ccmmapbase,
    .blksize = CCM_BLOCK_SIZE,
    .ramsize = CCM_MAX_SIZE,
    .mapsize = CCM_ALLOC_TABLE_SIZE,
    .ready = 0};

#if HEAP_USE_EXSRAM
/*EXT SRAM内存管理控制块*/
heap_dev_t __exsram = {
        .rambase = exsrambase,
        .mapbase = exsrammapbase,
        .blksize = EXSRAM_BLOCK_SIZE,
        .ramsize = EXSRAM_MAX_SIZE,
        .mapsize = EXSRAM_ALLOC_TABLE_SIZE,
        .ready = 0};
#endif
#if 0
 __attribute__((at(0X10000000 + CCM_MAX_SIZE + CCM_ALLOC_TABLE_SIZE)))
    __attribute__((at(0X10000000 + CCM_MAX_SIZE + CCM_ALLOC_TABLE_SIZE + sizeof(heap_dev_t))))
    __attribute__((at(0X10000000 + CCM_MAX_SIZE + CCM_ALLOC_TABLE_SIZE +
                      hpidx_max * sizeof(heap_dev_t)))) 
#endif
/*内存管理控制块列表*/
heap_dev_ptr heap_dev_list[]= {
        &__ccm
#if HEAP_USE_EXSRAM
        ,
        &__exsram
#endif
};

/**
 * @brief 内存拷贝
 * @param des:目的地址
 * @param src:源地址
 * @param n:需要复制的内存长度(字节为单位)
 */
static void my_memcpy(void *des, void *src, uint32_t n)
{
    uint8_t *xdes = des;
    uint8_t *xsrc = src;
    while (n--)
        *xdes++ = *xsrc++;
}
/**
 * @brief 设置内存
 * @param s:内存首地址
 * @param c:要设置的值
 * @param count:需要复制的内存长度(字节为单位)
 */
static void my_memset(void *s, uint8_t c, uint32_t count)
{
    uint8_t *xs = s;
    while (count--)
        *xs++ = c;
}

/**
 * @brief 内存管理初始化
 * @param idx:所属内存块 @heap_index_t
 * @return 0 成功 else 失败
 */
static int32_t my_mem_init(heap_index_t idx)
{
    heap_dev_ptr phd = NULL;
    if (idx >= hpidx_max)
    {
        return -1;
    }
    phd = heap_dev_list[idx];
    my_memset(phd->mapbase, 0, phd->mapsize * 2);
    my_memset(phd->rambase, 0, phd->ramsize);
    phd->ready = 1;
    return 0;
}

/**
 * @brief 获取内存使用率
 * @param idx:所属内存块
 * @return 使用率(0~10000)0.00%~100.00%
 */
uint16_t my_mem_perused(heap_index_t idx)
{
    uint32_t used = 0;
    uint32_t i;
    heap_dev_ptr phd = NULL;
    if (idx >= hpidx_max)
    {
        return 255;
    }
    phd = heap_dev_list[idx];
    if (!phd->ready)
        my_mem_init(idx); // 未初始化,先执行初始化
    for (i = 0; i < phd->mapsize; i++)
    {
        if (phd->mapbase[i])
            used++;
    }
    return (used * 10000) / phd->mapsize;
}

/**
 * @brief 释放内存(外部调用)
 * @param idx:所属内存块
 * @param ptr:内存首地址
 * @return None
 */
void my_free(heap_index_t idx, void *ptr)
{
    uint32_t offset;
    heap_dev_ptr phd = NULL;
    if (ptr == NULL)
        return; // 地址为0.
    if (idx >= hpidx_max)
    {
        return;
    }
    phd = heap_dev_list[idx];
    offset = (uint32_t)ptr - (uint32_t)phd->rambase;
    // 释放内存
    if (!phd->ready) // 未初始化,先执行初始化
    {
        my_mem_init(idx); // 未初始化,先执行初始化
        return;           // 未初始化
    }
    if (offset < phd->ramsize) // 偏移在内存池内.
    {
        int index = offset / phd->blksize; // 偏移所在内存块号码
        int nmemb = phd->mapbase[index];   // 内存块数量
        for (int i = 0; i < nmemb; i++)    // 内存块清零
        {
            phd->mapbase[index + i] = 0;
        }
        return;
    }
    else
        return; // 偏移超区了.
}

/**
 * @brief 分配内存(外部调用)
 * @param idx:所属内存块
 * @param size:内存大小(字节)
 * @return 分配到的内存首地址 为NULL(0)时分配失败
 */
void *my_malloc(heap_index_t idx, uint32_t size)
{
    signed long offset = 0;
    uint32_t nmemb;     // 需要的内存块数
    uint32_t cmemb = 0; // 连续空内存块数
    uint32_t i;
    heap_dev_ptr phd = NULL;
    if (idx >= hpidx_max)
    {
        return NULL;
    }
    if (size == 0)
        return NULL; // 不需要分配
    phd = heap_dev_list[idx];
    if (!phd->ready)
        my_mem_init(idx);        // 未初始化,先执行初始化
    nmemb = size / phd->blksize; // 获取需要分配的连续内存块数
    if (size % phd->blksize)
        nmemb++;
    for (offset = phd->mapsize - 1; offset >= 0; offset--) // 搜索整个内存控制区
    {
        if (!phd->mapbase[offset])
            cmemb++; // 连续空内存块数增加
        else
            cmemb = 0;      // 连续内存块清零
        if (cmemb == nmemb) // 找到了连续nmemb个空内存块
        {
            for (i = 0; i < nmemb; i++) // 标注内存块非空
            {
                phd->mapbase[offset + i] = nmemb;
            }
            offset = (offset * phd->blksize); // 返回偏移地址
            break;
        }
    }
    return (void *)((uint32_t)phd->rambase + offset);
}

/**
 * @brief 重新分配内存(外部调用)
 * @param idx:所属内存块
 * @param *ptr:旧内存首地址
 * @param size:内存大小(字节)
 * @return 新分配到的内存首地址 为NULL(0)时分配失败
 */
void *my_realloc(heap_index_t idx, void *ptr, uint32_t size)
{
    void *p;
    p = my_malloc(idx, size);
    if (p == NULL)
        return NULL;
    else
    {
        my_memcpy(p, ptr, size); // 拷贝旧内存内容到新内存
        my_free(idx, ptr);       // 释放旧内存
        return p;                // 返回新内存首地址
    }
}

void *ccm_malloc(uint32_t size)
{
    return my_malloc(hpidx_ccm, size);
}
void ccm_free(void *ptr)
{
    my_free(hpidx_ccm, ptr);
}
void *ccm_realloc(void *ptr, uint32_t size)
{
    return my_realloc(hpidx_ccm, ptr, size);
}
/**
 * @brief 获取CCM内存使用率
 * @return 使用率(0~10000)0.00%~100.00%
 */
uint16_t ccm_perused(void)
{
    return my_mem_perused(hpidx_ccm);
}

#if HEAP_USE_EXSRAM
void *exsram_malloc(uint32_t size)
{
    return my_malloc(hpidx_exsram, size);
}
void exsram_free(void *ptr)
{
    my_free(hpidx_exsram, ptr);
}
void *exsram_realloc(void *ptr, uint32_t size)
{
    return my_realloc(hpidx_exsram, ptr, size);
}
uint16_t exsram_perused(void)
{
    return my_mem_perused(hpidx_exsram);
}
#endif
